package org.timemachine.controller;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;

import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.persistence.Transient;
import javax.persistence.Version;

import org.timemachine.action.facade.EntityChangeFacade;
import org.timemachine.factory.EntityChangeFactory;
import org.timemachine.factory.spi.EntityChange;
import org.timemachine.model.base.ChangeInfo;
import org.timemachine.model.base.ForwardChangeInfo;
import org.timemachine.resource.Resources;
import org.timemachine.tool.ClassHelperImpl;
import org.timemachine.tool.FieldHelper;

@ManagedBean
@SessionScoped
public class TimeMachineEntityBean {

	@EJB
	private EntityChangeFacade entityChangeFacade;

	private List<String> columns;
	private List<Object[]> values;
	private Object[] objects;
	private int valueIndex;

	private List<String> columnsForward;
	private List<Object[]> valuesForward;
	private List<Boolean> columnsRenderingForward;
	
	private Object entity;

	public EntityChange getEntityChange(Object entity) {
		EntityChange entityChange = null;
		try {
			entityChange = entityChangeFacade.getEntityChange(entity);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return entityChange;
	}

	public Date getEntityCreateDate(Object entity) {
		EntityChange entityChange = getEntityChange(entity);
		if (entityChange != null) {
			return entityChange.getBeginChangeInfo().getChangeTimeStamp();
		} else {
			return null;
		}
	}

	public void fillEntityHistory(Object entity) throws IllegalArgumentException, IllegalAccessException {
		ClassHelperImpl cHelper = new ClassHelperImpl();
		Object entityId = cHelper.findPrimaryKeyValue(entity);

		List<EntityChange> list = (List<EntityChange>) entityChangeFacade.listHistoricalData(entity, entityId, null);
		if (list.size() == 0) {
			columns = null;
			values = null;
			return;
		}
		Field[] fields = cHelper.findAllDeclaredFieldsWithSuperClasses(list.get(0).getClass());

		columns = getFieldTitles(fields);
		values = new ArrayList<Object[]>();
		for (EntityChange entityChange : list) {
			objects = new Object[columns.size()];
			valueIndex = 0;
			for (Field field : fields) {
				field.setAccessible(true);
				if (controlField(field)) {
					Object object = field.get(entityChange);
					fillEntityChangeValues(field, object);
				}
			}
			values.add(objects);
		}
	}

	private boolean controlField(Field field) {
		if (Modifier.isStatic(field.getModifiers())) {
			return false;
		} else if (field.getName().equals(EntityChangeFactory.NAME_END_CHANGE_INFO)) {
			return false;
		//} else if (field.getName().equals(EntityChangeFactory.NAME_CHANGE_ID)) {
		//	return false;
		} else if (FieldHelper.hasAnnotation(field, Version.class)) {
			return false;
		} else if (FieldHelper.hasAnnotation(field, Transient.class)) {
			return false;
		}

		// } else if (FieldHelper.hasAnnotation(field, ManyToOne.class)) {
		// return false;
		return true;
	}

	private void fillEntityChangeValues(Field field, Object value) {
		// TODO date locale tr calismiyor

		DateFormat df = new SimpleDateFormat("dd/MM/yyyy", new Locale("tr", "TR"));
		if (field.getName().equals(EntityChangeFactory.NAME_CHANGE_TYPE)) {
			if (Integer.parseInt(value.toString()) == EntityChangeFactory.CHANGE_TYPE_INSERT) {
				objects[valueIndex++] = EntityChangeFactory.CHANGE_NAME_INSERT;
			} else if (Integer.parseInt(value.toString()) == EntityChangeFactory.CHANGE_TYPE_UPDATE) {
				objects[valueIndex++] = EntityChangeFactory.CHANGE_NAME_UPDATE;
			} else if (Integer.parseInt(value.toString()) == EntityChangeFactory.CHANGE_TYPE_REMOVE) {
				objects[valueIndex++] = EntityChangeFactory.CHANGE_NAME_REMOVE;
			}
		} else if (value instanceof ChangeInfo) {
			ChangeInfo changeInfo = (ChangeInfo) value;
			objects[valueIndex++] = df.format(changeInfo.getChangeTimeStamp());
			objects[valueIndex++] = changeInfo.getChangeUser();
			objects[valueIndex++] = changeInfo.getChannel();
		} else if (field.getType().isEnum()) {
			for (Field f: value.getClass().getDeclaredFields()) {
				try {
					if (!Modifier.isStatic(f.getModifiers())) {
					    f.setAccessible(true);
						objects[valueIndex++] = f.get(value);
					}
				} catch (Exception e) {
				}
			}
		} else {
			if (value instanceof BigDecimal) {
				NumberFormat nf = NumberFormat.getNumberInstance(new Locale("tr"));
				
				DecimalFormat decf = (DecimalFormat) nf;
				decf.setMaximumFractionDigits(2);
				decf.setMinimumFractionDigits(2);
				decf.setGroupingUsed(true);

				objects[valueIndex++] = decf.format(value);
			} else {
				objects[valueIndex++] = value;
			}
		}
	}

	private List<String> getFieldTitles(Field[] fields) {
		List<String> titles = new ArrayList<String>();
		Resources resources = Resources.getInstance("fields");
		for (Field field : fields) {
			if (controlField(field)) {
				if (field.getName().equals(EntityChangeFactory.NAME_CHANGE_TYPE)) {
					titles.add("KAYIT TİPİ");
				} else if (field.getName().equals(EntityChangeFactory.NAME_BEGIN_CHANGE_INFO)) {
					titles.add("KAYIT ZAMANI");
					titles.add("KAYIT KULLANICISI");
					titles.add("KAYIT KANAL");
				} else {
					String fieldTranslate = null;
					try {
						fieldTranslate = resources.getProperty(field.getName());
					} catch (MissingResourceException e) {
					}
					if (fieldTranslate != null) {
						titles.add(fieldTranslate);
					} else {
						titles.add(field.getName());
					}
				}
			}
		}
		return titles;
	}

	private List<Boolean> getFieldRenderings(Field[] fields) {
		List<Boolean> renderings = new ArrayList<Boolean>();
		Resources resources = Resources.getInstance("fieldsForward");
		for (Field field : fields) {
			if (controlField(field)) {
				String showField = null;
				try {
					showField = resources.getProperty(entity.getClass().getSimpleName()+"."+field.getName());
				} catch (MissingResourceException e) {
				}

				if (field.getName().equals(EntityChangeFactory.NAME_CHANGE_ID)) {
					renderings.add(Boolean.FALSE);
				} else if (field.getName().equals(EntityChangeFactory.NAME_BEGIN_CHANGE_INFO)) {
					renderings.add(Boolean.TRUE);
					renderings.add(Boolean.TRUE);
					renderings.add(Boolean.TRUE);
				} else if (showField != null && showField.equals("true")) {
					renderings.add(Boolean.TRUE);
				} else {
					renderings.add(Boolean.FALSE);
				}
			}
		}
		return renderings;
	}

	private int findChangeIdIndex() {
		int i=0;
		for (String title : getColumnsForward()) {
			if (title.equals(EntityChangeFactory.NAME_CHANGE_ID)) {
				return i;
			}
			i++;
		}
		return -1;
	}

	public Date findLastChangeTime(Object entity, String columnName) throws Exception {
		if (entity == null) {
			return null;
		}

		ClassHelperImpl cHelper = new ClassHelperImpl();

		Object orgValue = null;

		Field[] orgFields = cHelper.findAllDeclaredFieldsWithSuperClasses(entity.getClass());
		for (Field field : orgFields) {
			field.setAccessible(true);
			if (field.getName().equals(columnName)) {
				orgValue = field.get(entity);
				break;
			}
		}
		if (orgValue == null) {
			throw new Exception(columnName + " field is not fount int the " + entity.getClass().getSimpleName()
					+ " entity");
		}

		Object entityId = cHelper.findPrimaryKeyValue(entity);
		List<EntityChange> list = (List<EntityChange>) entityChangeFacade.listHistoricalData(entity, entityId, null);
		if (list.size() == 0) {
			return null;
		}

		Field changeField = null;
		Field[] fields = cHelper.findAllDeclaredFieldsWithSuperClasses(list.get(0).getClass());
		for (Field field : fields) {
			field.setAccessible(true);
			if (field.getName().equals(columnName)) {
				changeField = field;
				break;
			}
		}
		if (changeField == null) {
			throw new Exception(columnName + " field is not fount int the " + list.get(0).getClass().getSimpleName()
					+ " entity");
		}

		values = new ArrayList<Object[]>();
		Object changeValue = null;
		for (int i = list.size() - 1; i >= 0; i--) {
			EntityChange entityChange = list.get(i);
			changeValue = changeField.get(entityChange);
			if (!changeValue.equals(orgValue)) {
				return entityChange.getBeginChangeInfo().getChangeTimeStamp();
			}
		}
		return null;
	}

	public void fillEntityForward(Object entity) throws IllegalArgumentException, IllegalAccessException {
		this.entity = entity;
		
		ClassHelperImpl cHelper = new ClassHelperImpl();
		Object entityId = cHelper.findPrimaryKeyValue(entity);

		List<EntityChange> list = (List<EntityChange>) entityChangeFacade.listForwardData(entity, entityId, null);
		if (list.size() == 0) {
			columnsForward = null;
			columnsRenderingForward = null;
			valuesForward = null;
			return;
		}
		Field[] fields = cHelper.findAllDeclaredFieldsWithSuperClasses(list.get(0).getClass());

		columnsForward = getFieldTitles(fields);
		columnsRenderingForward = getFieldRenderings(fields);
		valuesForward = new ArrayList<Object[]>();
		for (EntityChange entityChange : list) {
			if (entityChange.getBeginChangeInfo().getClass().equals(ForwardChangeInfo.class)) {
				ForwardChangeInfo forwardChangeInfo = (ForwardChangeInfo) entityChange.getBeginChangeInfo();
				if (forwardChangeInfo.getProcessTimeStamp() == null) {
					objects = new Object[columnsForward.size()];
					valueIndex = 0;
					for (Field field : fields) {
						field.setAccessible(true);
						if (controlField(field)) {
							Object object = field.get(entityChange);
							fillEntityChangeValues(field, object);
						}
					}
					valuesForward.add(objects);
				}
			}
		}

	}

	public List<String> getColumns() {
		return columns;
	}

	public void setColumns(List<String> columns) {
		this.columns = columns;
	}

	public List<Object[]> getValues() {
		return values;
	}

	public void setValues(List<Object[]> values) {
		this.values = values;
	}

	public Object[] getObjects() {
		return objects;
	}

	public void setObjects(Object[] objects) {
		this.objects = objects;
	}

	public int getValueIndex() {
		return valueIndex;
	}

	public void setValueIndex(int valueIndex) {
		this.valueIndex = valueIndex;
	}

	public List<String> getColumnsForward() {
		return columnsForward;
	}

	public void setColumnsForward(List<String> columnsForward) {
		this.columnsForward = columnsForward;
	}

	public List<Boolean> getColumnsRenderingForward() {
		return columnsRenderingForward;
	}

	public void setColumnsRenderingForward(List<Boolean> columnsRenderingForward) {
		this.columnsRenderingForward = columnsRenderingForward;
	}

	public List<Object[]> getValuesForward() {
		return valuesForward;
	}

	public void setValuesForward(List<Object[]> valuesForward) {
		this.valuesForward = valuesForward;
	}
	
	public Object getEntity() {
		return entity;
	}

	public void setEntity(Object entity) {
		this.entity = entity;
	}

	public void deleteForward(Object[] entityForward, Object entity) {
		Integer changeId = (Integer) entityForward[findChangeIdIndex()];
		
		entityChangeFacade.deleteForwardEntityChange(changeId, entity);
	}

}